#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
         Instituto Politécnico de Beja
         Escolha Superior de Tecnologia e Gestão
         Mestrado em Engenharia de Segurança Informática
         Conceitos de Programação para a Segurança
Titulo:  Gerador de Números Pseudo-Aleatórios
Docente: Professor Doutor José Jasnau Caeiro
Autores: Daniel Franco nº 12062
         Manuel André Martins nº 12056
'''

'''
bibliotecas necessárias para gerar números aleatórios
e controlo do tempo de execução do programa
'''
import random
import time

'''
inicialização do relógio - ti(tempo inicial)
'''
ti = time.clock()

'''
função que gera uma tabela com 1000 posições e a preenche com números aleatórios entre 0 e 9
o número de cada digito é sempre igual, pelo que cada digito aparece 100 vezes
esta última condição é garantida pelo ciclo while que contabiliza o número de vezes que
cada digito vai aparecendo.

cont_digito - array para contar cada um dos digitos de 0 a 9
pos - posição da tabela onde o digito vai ser colocado
digito - digito calculado aleatóriamente
'''
def criar_tabela():
    tabela = [0 for i in range(0,1000)]
    cont_digito = [0 for x in range (0,10)]
    pos = 0
    while pos < 1000:
        digito =  random.randint(0,9)
        if (cont_digito[digito] > 99):
            continue
        cont_digito[digito] += 1
        tabela[pos] = digito
        pos += 1
    return tabela

'''
calcula o valor de p, aleatóriamente, entre 0 e 999
'''
def calculo_p():
    p = random.randint(0,999)
    return p

'''
troca de digitos da posição T[l] para T[0] e de T[0] para T[l], em que l admite
as posições p, p+1, p+2 e p+3
'''
def permuta(l):
    auxiliar = T[l]
    T[l] = T[0]
    T[0] = auxiliar

'''
calcula o novo valor de p, tendo em conta a fórmula
'''
def novo_p(z_1, z_2, z_3, z_4):
    p = (z_1 * 100) + (z_2 * 10) + (z_3 * 1)
    return p

'''
criação e preenchimento da tabela T
'''
T = criar_tabela()

'''
calcula o p inicial
'''
p = calculo_p()

'''
número de iterações a ser calculado, devendo este valor ser alterado consoante o número
desejado pelo utilizador
'''
iteracoes = 100

'''
calculo do número pseudo-aleatório de 16 bits
os primeiros 4 bits de Nk correspondem a z1
os segundos 4 bits de Nk correspondem a z2
os terceiros 4 bits de Nk correspondem a z3
os quartos 4 bits de Nk correspondem a z4

Nk = z1 << 12 | z2 << 8 | z3 << 4 | z4

a cada ciclo é mostrado o número pseudo-aleatório calculado e realiza-se um novo calculo de p
a cada ciclo, é decrementado 1 às iterações, até que seja atingido o valor 0 e finalizado
o programa
'''
while iteracoes > 0:
    if p < 1000:
        l = p
        z1 = T[l]
    if p >= 1000:
        l = 0
        z1 = T[l]
    permuta(l)

    if p < 999:
        l = p + 1
        z2 = T[l]
    if p >= 999:
        l = 0
        z2 = T[l]
    permuta(l)

    if p < 998:
        l = p + 2
        z3 = T[l]
    if p >= 998:
        l = 0
        z3 = T[l]
    permuta(l)
        
    if p < 997:
        l = p + 3
        z4 = T[l]
    if p >= 997:
        l = 0;
        z4 = T[l]
    permuta(l)
    Nk = z1 << 12 | z2 << 8 | z3 << 4 | z4
    print 'N = ', Nk
    p = novo_p(z1, z2, z3, z4)
    iteracoes -= 1

'''
calcula-se o tempo total decorrido (tt), subtraindo ao tempo final (tf) o tempo inicial (ti)
'''
tf = time.clock()
tt = tf - ti
print 'Tempo Decorrido: ', tt, 'segundos'

'''
Resultados dos tempos obtidos:
MacBook Pro Intel Core 2 Duo - 2.26Ghz - 8GB DDRIII

10    Números: 0,01 segundos
100   Números: 0,15 segundos
1000  Números: 1,56 segundos
10000 Números: 123,24 segundos
'''
